/* 
	main.c		09/2006
	Eric Agan	slax0r.hax0r@gmail.com
	
	Discrete Wave Transform / Haar Transform demo application
	
	Last Updated: September 25, 2006
	
	Update History:
	24092006 - Started cleaning up the code.. (eric)
	xx092006 - Updated code for easy cross-platform compiling. (eric)
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <SDL/SDL.h>

Uint32	getpixel(SDL_Surface *surface, Uint32 x, Uint32 y);
void	haar_transform(SDL_Surface *screen, int sx, int sy, int w, int h, int where_x, int where_y);

/* set pixel (x,y) to given value */
void putpixel(SDL_Surface *surface, Uint32 x, Uint32 y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* p is the addy to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;
    case 2:
        *(Uint16 *)p = pixel;
        break;
    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;
    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

/* returns pixel value at (x,y) */
Uint32 getpixel(SDL_Surface *surface, Uint32 x, Uint32 y)
{
    int bpp = surface->format->BytesPerPixel;
    /* p is the addy to the pixel we want to retrieve */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        return *p;
    case 2:
        return *(Uint16 *)p;
    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;
    case 4:
        return *(Uint32 *)p;
    default:
        return 0;       /* dodge warnings */
    }
}

/* our haar transform implementation fresh from the oven */
void haar_transform(SDL_Surface *screen, int sx, int sy, int w, int h, int where_x, int where_y) {
	// so we need to input the image, get 2x2 pixel blocks -> block[]
	// where block is 0-3, corresponding to:
	// [a b]  matrix, and thus output = 0.5*(block[a]+block[b]+block[c]+block[d])
	// [c d]    which is a 4-point average..
	int x, y;
	Uint32 r, g, b, a;
	Uint8  rt, gt, bt, at;
	for(x=sx; x<(sx+w); x+=2) {		// two-step
		for(y=sy; y<(sy+h); y+=2) {
			// get the color in RGBA format for each pixel in the 2x2 matrix
			SDL_GetRGBA(getpixel(screen,x,y),screen->format,&rt,&gt,&bt,&at);		// 0,0
			r=rt;	b=bt;	g=gt;	a=at;
			SDL_GetRGBA(getpixel(screen,x+1,y),screen->format,&rt,&gt,&bt,&at);		// 1,0
			r+=rt;	b+=bt;	g+=gt;	a+=at;
			SDL_GetRGBA(getpixel(screen,x,y+1),screen->format,&rt,&gt,&bt,&at);		// 0,1
			r+=rt;	b+=bt;	g+=gt;	a+=at;
			SDL_GetRGBA(getpixel(screen,x+1,y+1),screen->format,&rt,&gt,&bt,&at);	// 1,1
			r+=rt;	b+=bt;	g+=gt;	a+=at;
			// 4-point average so 1/4 or 0.25 is the factor
			r *= 0.25;	// it's an average, so.. do this..
			b *= 0.25;
			g *= 0.25;
			a *= 0.25;
			// put the pixel on the "screen" surface at the specified location
			putpixel(screen, where_x+((x-sx)/2), (y-sy)/2, SDL_MapRGBA(screen->format, r, g, b, a));
		}
	}
}

#ifdef main
#undef main
#endif
#ifdef _main
#undef _main
#endif

int main(int argc, char *argv[])
{
	SDL_Surface *bitmap, *screen;

	/* the following code is SDL initialization and prep work.. */

	// initialize the video
	if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
		fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
		exit(1);
	}
	atexit(SDL_Quit);
	
	// let's open our example.bmp file..
	if((bitmap = SDL_LoadBMP("example.bmp")) == NULL) {
		fprintf(stderr, "Unable to load BMP: %s\n", SDL_GetError());
		exit(1);
	}

	// make our screen 2x the width and the same height as the bitmap..
	if( !(screen = SDL_SetVideoMode(bitmap->w*2, bitmap->h, 32, SDL_SWSURFACE)) ) {
		fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
		exit(1);
	}
	
	// now blit the image to the window..
	if(SDL_BlitSurface(bitmap, NULL, screen, NULL) != NULL) {
		fprintf(stderr, "Unable to blit BMP: %s\n", SDL_GetError());
		exit(1);
	}
	SDL_UpdateRect(screen, 0, 0, 0, 0);
	
	// now let's use our DWT function to shrink it!
	// first we lock the screen for editing..
	if(SDL_LockSurface(screen) != NULL) {
		fprintf(stderr, "SHIT! We can't lock the surface for pixel-level work: %s\n", SDL_GetError());
		exit(1);
	}

	/* here's the good stuff.. how to use the haar_transform() function we have here */
	
	// cycle through our image's pixels, feed them to the hungry DWT function..
	// werd.. the Haar Transform at work, LL filter only, HL HH and LH can go fsck themselves..
	// hardcore. t(@.@t)
	haar_transform(screen, 0, 0, bitmap->w, bitmap->h, bitmap->w, 0);
	haar_transform(screen, bitmap->w, 0, bitmap->w/2, bitmap->h/2, bitmap->w*1.5, 0);
	haar_transform(screen, bitmap->w*1.5, 0, bitmap->w/4, bitmap->h/4, bitmap->w*1.75, 0);
	
	// now we unlock the screen & update it..
	SDL_UnlockSurface(screen);
	SDL_UpdateRect(screen, 0, 0, 0, 0);
	
	// when they hit the X, kill it.
	SDL_Event event;
	while( event.type != SDL_QUIT ) 
		SDL_PollEvent(&event);

	
	printf("later, dude!\n");
	exit(0);
}
